﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace DAO.DAO
{
    public class BaseDao<T> where T : class
    {
        protected DataDBContext _DataDBContext = new DataDBContext();

        /// <summary>
        /// 查询
        /// </summary>
        /// <returns>返回IQueryable、IOrderedQueryable</returns>
        public virtual IQueryable<T> FindAll()
        {
            return _DataDBContext.Set<T>();
        }
		
        /// <summary>
        /// 查询(无缓存)
        /// </summary>
        /// <returns>返回IQueryable、IOrderedQueryable</returns>
        public virtual IQueryable<T> FindAllNoCatch()
        {
            return _DataDBContext.Set<T>().AsNoTracking();
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="predicate">根据条件</param>
        /// <returns>返回IQueryable、IOrderedQueryable</returns>
        public virtual IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
        {
            return _DataDBContext.Set<T>().Where(predicate);
        }

        /// <summary>
        /// 查询(无缓存)
        /// </summary>
        /// <param name="predicate">根据条件</param>
        /// <returns>返回IQueryable、IOrderedQueryable</returns>
        public virtual IQueryable<T> FindByNoCatch(Expression<Func<T, bool>> predicate) {
            return _DataDBContext.Set<T>().AsNoTracking().Where(predicate);
        }

        /// <summary>
        /// 根据ID查询
        /// </summary>
        /// <param name="primaryKey">主键</param>
        /// <returns>返回查询出的对象</returns>
        public virtual T FindByKey(params object[] primaryKey)
        {
            return _DataDBContext.Set<T>().Find(primaryKey);
        }

        /// <summary>
        /// 新增
        /// </summary>
        /// <param name="po">PO对象</param>
        /// <returns>返回新增成功与否</returns>
        public virtual bool Create(T po)
        {
            if (po == null)
                throw new ArgumentNullException("po");
            _DataDBContext.Entry(po).State = EntityState.Added;
            if (_DataDBContext.SaveChanges() > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 批量增加
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public virtual bool AddRange(IEnumerable<T> entities)
        {
            try
            {
                _DataDBContext.Set<T>().AddRange(entities);
                return _DataDBContext.SaveChanges() > 0;
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        /// <summary>
        /// 修改(非部分修改)
        /// </summary>
        /// <param name="po">对象</param>
        /// <returns>返回成功或者失败</returns>
        public virtual bool Update(T po)
        {
            if (po == null)
                throw new ArgumentNullException("po");
            _DataDBContext.Entry(po).State = EntityState.Modified;
            if (_DataDBContext.SaveChanges() > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 部分修改
        /// </summary>
        /// <param name="po">要修改的实体对象</param>
        /// <param name="proNames">要修改的 属性 名称</param>
        /// <returns>返回成功或者失败</returns>
        public virtual bool Update(T po, params string[] proNames)
        {
            if (po == null)
                throw new ArgumentNullException("po");
            //将 对象 添加到 EF中
            var entry = _DataDBContext.Entry<T>(po);
            //先设置 对象的包装 状态为 Unchanged
            entry.State = EntityState.Unchanged;
            //循环 被修改的属性名 数组
            foreach (string proName in proNames)
            {
                //将每个 被修改的属性的状态 设置为已修改状态;后面生成update语句时，就只为已修改的属性 更新
                entry.Property(proName).IsModified = true;
            }
            //一次性 生成sql语句到数据库执行
            if (_DataDBContext.SaveChanges() > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 部分条件修改
        /// </summary>
        /// <param name="model"></param>
        /// <param name="whereLambda"></param>
        /// <param name="modifiedProNames"></param>
        /// <returns></returns>
        public bool UpdateBy(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames)
        {
            //查询要修改的数据
            List<T> listModifing = _DataDBContext.Set<T>().Where(whereLambda).ToList();
            //获取 实体类 类型对象
            Type t = typeof(T); // model.GetType();
            //获取 实体类 所有的 公有属性
            List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            //创建 实体属性 字典集合
            Dictionary<string, PropertyInfo> dictPros = new Dictionary<string, PropertyInfo>();
            //将 实体属性 中要修改的属性名 添加到 字典集合中 键：属性名  值：属性对象
            proInfos.ForEach(p =>
            {
                if (modifiedProNames.Contains(p.Name))
                {
                    dictPros.Add(p.Name, p);
                }
            });
            //4.3循环 要修改的属性名
            foreach (string proName in modifiedProNames)
            {
                //判断 要修改的属性名是否在 实体类的属性集合中存在
                if (dictPros.ContainsKey(proName))
                {
                    //如果存在，则取出要修改的 属性对象
                    PropertyInfo proInfo = dictPros[proName];
                    //取出 要修改的值
                    object newValue = proInfo.GetValue(model, null); //object newValue = model.uName;
                    //批量设置 要修改 对象的 属性
                    foreach (T usrO in listModifing)
                    {
                        //为 要修改的对象 的 要修改的属性 设置新的值
                        proInfo.SetValue(usrO, newValue, null); //usrO.uName = newValue;
                    }
                }
            }
            //一次性 生成sql语句到数据库执行
            if (_DataDBContext.SaveChanges() > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="primaryKey">主键</param>
        /// <returns>返回成功或者失败</returns>
        public virtual bool Delete(params object[] primaryKey)
        {
            T model = _DataDBContext.Set<T>().Find(primaryKey);
            if (model != null)
            {
                _DataDBContext.Entry(model).State = EntityState.Deleted;
                if (_DataDBContext.SaveChanges() > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 条件删除
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual bool DeleteBy(Expression<Func<T, bool>> predicate)
        {
            T[] entities = _DataDBContext.Set<T>().Where(predicate).ToArray();
            if (entities.Length == 0) return false;
            _DataDBContext.Set<T>().RemoveRange(entities);
            return _DataDBContext.SaveChanges() > 0;
        }

        /// <summary>
        /// 是否存在
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual bool Exist(Expression<Func<T, bool>> predicate)
        {
            if (_DataDBContext.Set<T>().Where(predicate).Any())
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}